package eval

import (
	"showen7/eval"
	"showen7/inter"
	"showen7/lex"
	"showen7/parse"
	"testing"
)

func TestEqPredicate(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(eq? #t #t)", "#t"},
		{"(eq? #f #f)", "#t"},
		{"(eq? #t #f)", "#f"},
		{"(eq? #f #t)", "#f"},
		{"(eq? 'symbol 'symbol)", "#t"},
		{"(eq? 'symbol 'another)", "#f"},
		{"(eq? '() '())", "#t"},               // Same empty list object
		{"(eq? \"string\" \"string\")", "#f"}, // Different string objects
		{"(eq? 123 123)", "#f"},               // Different number objects
		{"(eq? #\\a #\\a)", "#f"},             // Different character objects
		{"(eq? #\\a #\\b)", "#f"},             // Different character objects
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		var obj inter.Object
		obj, err = eval.EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("EvalExpression(%s) = %v, want %v", tt.input, obj.Inspect(), tt.want)
		}
	}
}

func TestEqPredicateWithSameObject(t *testing.T) {
	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	// Create a symbol and test with the same object reference
	env.Set("sym", &inter.SymbolObject{Value: "test"})

	lexer := lex.NewLexer("(eq? sym sym)")
	tokens := lexer.ReadTokens()
	parser := parse.NewParser(tokens)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser.ParseExpression() error = %v", err)
	}

	obj, err := eval.EvalExpression(expr, env)
	if err != nil {
		t.Fatalf("EvalExpression() error = %v", err)
	}

	if obj.Inspect() != "#t" {
		t.Errorf("EvalExpression((eq? sym sym)) = %v, want #t", obj.Inspect())
	}
}

func TestEqPredicateError(t *testing.T) {
	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	// Test with wrong number of arguments
	lexer := lex.NewLexer("(eq? #t)")
	tokens := lexer.ReadTokens()
	parser := parse.NewParser(tokens)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser.ParseExpression() error = %v", err)
	}

	_, err = eval.EvalExpression(expr, env)
	if err == nil {
		t.Errorf("Expected error for wrong number of arguments, but got none")
	}
}
func TestEqvPredicate(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(eqv? #t #t)", "#t"},
		{"(eqv? #f #f)", "#t"},
		{"(eqv? #t #f)", "#f"},
		{"(eqv? #f #t)", "#f"},
		{"(eqv? 'symbol 'symbol)", "#t"},
		{"(eqv? 'symbol 'another)", "#f"},
		{"(eqv? '() '())", "#t"},                // Same empty list object
		{"(eqv? \"string\" \"string\")", "#t"},  // Same string values
		{"(eqv? \"string\" \"another\")", "#f"}, // Different string values
		{"(eqv? 123 123)", "#t"},                // Same number values
		{"(eqv? 123 456)", "#f"},                // Different number values
		{"(eqv? #\\a #\\a)", "#t"},              // Same character values
		{"(eqv? #\\a #\\b)", "#f"},              // Different character values
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		var obj inter.Object
		obj, err = eval.EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("EvalExpression(%s) = %v, want %v", tt.input, obj.Inspect(), tt.want)
		}
	}
}

func TestEqualPredicate(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(equal? #t #t)", "#t"},
		{"(equal? #f #f)", "#t"},
		{"(equal? #t #f)", "#f"},
		{"(equal? #f #t)", "#f"},
		{"(equal? 'symbol 'symbol)", "#t"},
		{"(equal? 'symbol 'another)", "#f"},
		{"(equal? '() '())", "#t"},                // Same empty list object
		{"(equal? \"string\" \"string\")", "#t"},  // Same string values
		{"(equal? \"string\" \"another\")", "#f"}, // Different string values
		{"(equal? 123 123)", "#t"},                // Same number values
		{"(equal? 123 456)", "#f"},                // Different number values
		{"(equal? #\\a #\\a)", "#t"},              // Same character values
		{"(equal? #\\a #\\b)", "#f"},              // Different character values
		{"(equal? '(1 2 3) '(1 2 3))", "#t"},      // Same list structures
		{"(equal? '(1 2 3) '(1 2 4))", "#f"},      // Different list structures
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		var obj inter.Object
		obj, err = eval.EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("EvalExpression(%s) = %v, want %v", tt.input, obj.Inspect(), tt.want)
		}
	}
}

func TestEqvPredicateWithNumbers(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(eqv? 1 1)", "#t"},
		{"(eqv? 1 2)", "#f"},
		{"(eqv? 1.0 1.0)", "#t"},
		{"(eqv? 1.0 2.0)", "#f"},
		{"(eqv? 1/2 1/2)", "#t"},
		{"(eqv? 1/2 1/3)", "#f"},
		// Note: Cross-type comparisons might not work as expected depending on implementation
	}

	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		obj, err := eval.EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("EvalExpression() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("EvalExpression(%s) = %v, want %v", tt.input, obj.Inspect(), tt.want)
		}
	}
}

func TestEqvPredicateWithSameObject(t *testing.T) {
	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	// Create a symbol and test with the same object reference
	env.Set("sym", &inter.SymbolObject{Value: "test"})

	lexer := lex.NewLexer("(eqv? sym sym)")
	tokens := lexer.ReadTokens()
	parser := parse.NewParser(tokens)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser.ParseExpression() error = %v", err)
	}

	obj, err := eval.EvalExpression(expr, env)
	if err != nil {
		t.Fatalf("EvalExpression() error = %v", err)
	}

	if obj.Inspect() != "#t" {
		t.Errorf("EvalExpression((eqv? sym sym)) = %v, want #t", obj.Inspect())
	}
}

func TestEqvPredicateError(t *testing.T) {
	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	// Test with wrong number of arguments
	lexer := lex.NewLexer("(eqv? #t)")
	tokens := lexer.ReadTokens()
	parser := parse.NewParser(tokens)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser.ParseExpression() error = %v", err)
	}

	_, err = eval.EvalExpression(expr, env)
	if err == nil {
		t.Errorf("Expected error for wrong number of arguments, but got none")
	}
}

func TestEqualPredicateWithSameObject(t *testing.T) {
	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	// Create a symbol and test with the same object reference
	env.Set("sym", &inter.SymbolObject{Value: "test"})

	lexer := lex.NewLexer("(equal? sym sym)")
	tokens := lexer.ReadTokens()
	parser := parse.NewParser(tokens)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser.ParseExpression() error = %v", err)
	}

	obj, err := eval.EvalExpression(expr, env)
	if err != nil {
		t.Fatalf("EvalExpression() error = %v", err)
	}

	if obj.Inspect() != "#t" {
		t.Errorf("EvalExpression((equal? sym sym)) = %v, want #t", obj.Inspect())
	}
}

func TestEqualPredicateError(t *testing.T) {
	env := inter.NewEnvironment()
	eval.RegisterBuiltinProcedures(env)

	// Test with wrong number of arguments
	lexer := lex.NewLexer("(equal? #t)")
	tokens := lexer.ReadTokens()
	parser := parse.NewParser(tokens)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser.ParseExpression() error = %v", err)
	}

	_, err = eval.EvalExpression(expr, env)
	if err == nil {
		t.Errorf("Expected error for wrong number of arguments, but got none")
	}
}
